#include "includes.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define SAMPLE_CNT      10
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
void USER_ChangePwmOut(unsigned char duty);

extern TIM_HandleTypeDef htim1;
extern ADC_HandleTypeDef hadc;
/**
  * @brief  Configure the TIM Output pwm.
  * @param  None
  * @retval None
  */
void USER_App(void)
{
  GPIO_PinState status_zcd;
  do{
    status_zcd = HAL_GPIO_ReadPin(ZCD_GPIO_Port, ZCD_Pin);
  }while(status_zcd != GPIO_PIN_SET);
  
  USER_ChangePwmOut(50);
  delay_ms(2000); // delay 2s
}

/**
  * @brief  detect ZCD, NOW USE SYSTICK COUNTER, or use a timer
  * @param  None
  * @retval ZCD freq
  */
unsigned int USER_DetectZcdFreq(void)
{
  unsigned int freq;
  // start sample 
  
  // calc freq
  
  // return value
  return freq;
}
/**
  * @brief  main loop function.
  * @param  Duty
  * @retval None
  */
void USER_ChangePwmOut(unsigned char duty)
{
  TIM_OC_InitTypeDef sConfigOC;
  
  uint32_t pulse;
  pulse = (uint32_t)(USER_PERIOD_VALUE*duty/100);
  
  // stop pwm out 
  if (HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_2) != HAL_OK)
  {
    /* PWM stop Error */
    Error_Handler();
  }
  // re-config pwm out prar
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = pulse;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
  sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
  // start pwm out
  if (HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2) != HAL_OK)
  {
    /* PWM Generation Error */
    Error_Handler();
  }
}


static int small_compare(const void * a, const void * b)
{
  return *(uint32_t *)a > *(uint32_t *)b ? 1 : -1; 
}
/**
  * @brief  V_LED PID regulator function.
  * @param  None
  * @retval None
  */

static uint32_t User_SampleFilter(uint32_t *pDin, uint32_t size)
{
  uint8_t i;
  uint32_t sum = 0;
  
  if(size < 10)
    return sum;
  
  qsort(pDin, size, sizeof(uint32_t), small_compare);
  for(i = 1; i < (size-1); i++){
      sum += pDin[i]; 
  }
  sum /= (size -2);
  
  return sum;
}

/**
  * @brief  V_LED PID regulator function.
  * @param  None
  * @retval None
  */
uint32_t User_GetAdcValue(uint32_t adc_channel)
{
  uint32_t value;
  uint32_t sample[SAMPLE_CNT] = {0};
  ADC_ChannelConfTypeDef sConfig;
  
  sConfig.Channel      = adc_channel;
  sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
  sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
  
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
  if (HAL_ADC_Start(&hadc) != HAL_OK)
  {
    /* Start Conversation Error */
    Error_Handler();
  }
  
  for(uint8_t i = 0; i < SAMPLE_CNT; i++){
    if (HAL_ADC_PollForConversion(&hadc, 10) != HAL_OK)
    {
      /* End Of Conversion flag not set on time */
      Error_Handler();
    }
    else
    {
      /* ADC conversion completed */
      sample[i] = HAL_ADC_GetValue(&hadc);
    }
  }
  value = User_SampleFilter(sample, SAMPLE_CNT) * 3300/4096; // 3.3v trans mv
  
  return value;
}

/**
  * @brief  V_LED PID regulator function.
  * @param  None
  * @retval None
  */
void User_VledPIDRegulator(void)
{
  uint32_t V_LED;
  int16_t pid_result;
  for(uint8_t i = 0; i < 10; i++){
    V_LED = User_GetAdcValue(USER_V_LED_CHANNEL);
    // call pid algorithm
    pid_result = PID_realize(V_LED, 0);
    // adjust pwm output
  }
}

/**
  * @brief  I_LED PID regulator function.
  * @param  None
  * @retval None
  */
void User_IledPIDRegulator(void)
{
  uint32_t DIM, I_LED;
  int16_t pid_result;
  for(uint8_t i = 0; i < 10; i++){
    DIM = User_GetAdcValue(USER_DIM_CHANNEL);
    I_LED = User_GetAdcValue(USER_I_LED_CHANNEL);
    // call pid algorithm
    pid_result = PID_realize(I_LED, DIM);
    // adjust pwm output
    
  }
}


/**
  * @brief  main loop function.
  * @param  None
  * @retval None
  */
void USER_MainLoop(void)
{
  // detect on_ff and s_on_off
  GPIO_PinState status_on_off, status_s_on_off;
  uint8_t valid_cnt = 0;
  
  for(uint8_t i = 0; i < 5; i++){
    status_on_off = HAL_GPIO_ReadPin(ON_OFF_GPIO_Port, ON_OFF_Pin);
    status_s_on_off = HAL_GPIO_ReadPin(S_ON_OFF_GPIO_Port, S_ON_OFF_Pin);
    if(status_on_off && status_s_on_off){
      valid_cnt++;
    }
    delay_ms(5);
  }
  
  if(valid_cnt > 3){
    User_IledPIDRegulator();
  }
  else{
    User_VledPIDRegulator();
  }
}